This file will look at changing the default theme of ggplot()
, adding custom annotations, and combining multiple plots into a single figure. Also, some very basics of making tables!
# libraries
library(tidyverse)
library(tigris)
library(ggthemes)
library(patchwork)
library(knitr)
library(kableExtra)
library(DT)
# data
gag <- readRDS("data/gaglaws.RDS")
gag_long <- readRDS("data/gaglaws_long.RDS")
states48 <- readRDS("data/state_shapefile.RDS")
The example data was collected by PEN America to understand the ongoing state legislative efforts to censor America’s classrooms.
Let’s start with a basic figure, a barplot of the number of educational gag bills introduced in the past year within each state.
# create state-level gag order df
gag_state <- gag %>%
group_by(State) %>%
summarize(numbills = sum(target_k12))
# Initial figure
ggplot(gag_state, aes(y = fct_reorder(State, numbills), x = numbills)) +
geom_col() +
labs(x = "", y = "", title = "Gag Laws Targeting K-12 Education")
There are two primary ways to change the overall look or theme of the plot:
theme()
function and change individual elementsThe following version does both – using theme_few()
from ggthemes and making the y-axis text a little smaller.
# Changing theme (and adding color)
ggplot(gag_state, aes(y = fct_reorder(State, numbills), x = numbills)) +
geom_col(fill = "slategray3") +
labs(x = "", y = "", title = "Gag Laws Targeting K-12 Education") +
theme_few() + # theme_minimal
theme(axis.text.y = element_text(size = 8))
Schwabish and others also suggested using additional text on the figures to provide context or otherwise support interpretation. We can do that with the annotate()
function!
# n_distinct(gag$State) # value for annotation
ggplot(gag_state, aes(y = fct_reorder(State, numbills), x = numbills)) +
geom_col(fill = "slategray4") +
labs(x = "", y = "", title = "") +
annotate("text", x = 15, y = 15, color = "slategray", size = 10,
label = "42 states had at least one bill\n introduced to limit teaching\n in K-12 schools in 2021-2022") +
theme_few() +
theme(axis.text.y = element_text(size = 10))
Schwabish, in particular, also advised that a map – while pretty cool looking – may not be sufficient on it’s own if we want readers to better understand the values being presented on a choropleth map. So let’s make a map and them combine it with the bar plot above.
# join gag_state to states
gag_state_sf <- left_join(states48, gag_state,
by = c("NAME" = "State"))
gag_state_sf <- gag_state_sf %>%
mutate(numbills = replace_na(numbills, 0))
gag_state_sf <- gag_state_sf %>%
mutate(binbills = cut(numbills, right = FALSE,
breaks = c(0, 1, 3, 6, 10, 15, 25),
labels = c("0", "1-2", "3-5", "6-9",
"10-14", "15-25"),
ordered_result = TRUE))
ggplot(gag_state_sf) +
geom_sf(aes(fill = binbills)) +
scale_fill_viridis_d(option = "plasma", name = "# Bills") +
theme_void()
To put them together, we can use patchwork!
# to patch, we need to save the figures we want to combine as objects
bar <- gag_state_sf %>% filter(numbills > 0) %>%
ggplot(aes(x = fct_rev(fct_reorder(NAME, numbills)), y = numbills)) +
geom_col(aes(fill = binbills)) +
scale_fill_viridis_d(option = "plasma", guide = "none") +
labs(x = "", y = "", title = "") +
annotate("text", x = 20, y = 5, color = "slategray", hjust = 0, size = 8,
label = "42 states had at least one bill\n introduced to limit teaching\n in K-12 schools in 2021-2022") +
theme_solid() +
theme(axis.text.x = element_text(size = 12, angle = 45, hjust = 1, vjust = 1.5))
map <- ggplot(gag_state_sf) +
geom_sf(aes(fill = binbills)) +
scale_fill_viridis_d(option = "plasma", name = "# Bills") +
theme_void() +
theme(legend.position = "bottom")
# bar + map +
# plot_annotation(title = "Number of Gag Bills Targeting K-12 Education",
# caption = "Data collected by PEN America: https://pen.org/report/americas-censored-classrooms/")
# map / bar +
# plot_annotation(title = "Number of Gag Bills Targeting K-12 Education",
# caption = "Data collected by PEN America: https://pen.org/report/americas-censored-classrooms/")
bar + inset_element(map, .1, .3, 1, 1) +
plot_annotation(title = "Number of Gag Bills Targeting K-12 Education",
caption = "Data collected by PEN America: https://pen.org/report/americas-censored-classrooms/")
Sometimes we just want to present data in a table – if it’s a small amount of data (limited rows, limited columns), the kable and kableExtra packages are useful and easy
gag_state %>%
kbl(align = "c") %>%
kable_styling(bootstrap_options = c("striped", "hover")) %>%
scroll_box(height = "300px")
State | numbills |
---|---|
Alabama | 5 |
Alaska | 4 |
Arizona | 7 |
Arkansas | 5 |
Colorado | 1 |
Connecticut | 1 |
Florida | 3 |
Georgia | 5 |
Idaho | 4 |
Illinois | 2 |
Indiana | 8 |
Iowa | 6 |
Kansas | 4 |
Kentucky | 6 |
Louisana | 1 |
Louisiana | 3 |
Maryland | 1 |
Michigan | 2 |
Minnesota | 2 |
Mississippi | 10 |
Missouri | 24 |
Nebraska | 1 |
New Hampshire | 2 |
New Jersey | 3 |
New Mexico | 1 |
New York | 2 |
North Carolina | 4 |
North Dakota | 1 |
Ohio | 3 |
Oklahoma | 11 |
Pennsylvania | 3 |
Rhode Island | 1 |
South Carolina | 9 |
South Dakota | 4 |
Tennessee | 4 |
Texas | 5 |
Utah | 1 |
Virginia | 7 |
Washington | 2 |
West Virginia | 7 |
Wisconsin | 1 |
Wyoming | 2 |
If we’re trying to show a lot of data, or make it easy for readers to view (part of) the raw data themselves, the datatable package is a good place to start.
# Make columns individually filterable, change pagelength
gag %>%
select(c(1:5, 7,8)) %>%
datatable(filter = 'top', options = list(pageLength = 5),
colnames = c('State', 'Bill Number', 'Date', 'Status', 'Sponsor', 'Targets', 'Enforcement'),
caption = 'Data collected by PEN America: https://pen.org/report/americas-censored-classrooms/')